package com.api.client.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Verb;
import org.scribe.utils.StreamUtils;

import com.api.client.model.ApiRequest;
import com.api.client.model.ApiResponse;

public class ApiScribeRequest implements ApiRequest {
	private static final int DEFAULT_BUFFER_SIZE = 1024;

	private final OAuthRequest request;

	private final String method;

	private Map<String, String> queryParams = new HashMap<String, String>();

	private Map<String, String> formParams = new HashMap<String, String>();

	public ApiScribeRequest(String method, String url) {

		if (method == null || url == null) {
			throw new IllegalArgumentException("Method and url cannot be null");
		}
		this.method = method;
		Verb verb = Verb.valueOf(method);
		this.request = new OAuthRequest(verb, url);
	}

	@Override
	public String getMethod() {
		return request.getVerb().name();
	}

	@Override
	public String getUrl() {
		return request.getUrl();
	}

	@Override
	public Map<String, String> getHeaders() {
		return request.getHeaders();
	}

	@Override
	public Map<String, String> getQueryParams() {
		return Collections.unmodifiableMap(queryParams);
	}

	@Override
	public Map<String, Object> getFormParams() {

		Map<String, Object> map = new HashMap<String, Object>();

		for (Entry<String, String> entry : formParams.entrySet()) {
			map.put(entry.getKey(), entry.getValue());
		}
		return map;
	}

	@Override
	public ApiResponse send() {

		RequestLogger.logRequest(this);

		Response response = request.send();

		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			if (response.getStream() != null) {
				byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
				int len;
				while ((len = response.getStream().read(buffer)) > -1) {
					baos.write(buffer, 0, len);
				}
				baos.flush();
			}
			InputStream stream = new ByteArrayInputStream(baos.toByteArray());
			String body = StreamUtils.getStreamContents(stream);

			InputStream copiedStream = new ByteArrayInputStream(baos.toByteArray());

			ApiResponse apiResponse = new ApiResponseImpl(method, request.getUrl(), response.getCode(), body, copiedStream);
			RequestLogger.logResponse(apiResponse);
			return apiResponse;

		} catch (IOException e) {

			throw new IllegalStateException("Couldn't make copy of input stream", e);
		}
	}

	public void addQueryValues(Map<String, String> values) {
		queryParams.putAll(values);

		for (Entry<String, String> entry : values.entrySet()) {
			request.addQuerystringParameter(entry.getKey(), entry.getValue());
		}
	}

	public void addFormValues(Map<String, String> values) {
		formParams.putAll(values);

		for (Entry<String, String> entry : values.entrySet()) {
			request.addBodyParameter(entry.getKey(), entry.getValue());
		}
	}

	public void sign(OAuthProvider provider, String accessKey, String accessSecret) {
		provider.sign(request, accessKey, accessSecret);
	}

	public void addHeaderValues(Map<String, String> values) {
		for (Entry<String, String> entry : values.entrySet()) {
			request.addHeader(entry.getKey(), entry.getValue());
		}
	}

	@Override
	public String getHeader(String name) {
		if (!request.getHeaders().containsKey(name)) {
			throw new IllegalArgumentException(String.format("Header '%s' doesn't exist in request", name));
		}
		return request.getHeaders().get(name);
	}
}
